import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import java.awt.Font;

public class driver extends JFrame implements ActionListener {
	
	private static final long serialVersionUID = 1L;
	private static JPanel contentPane;
	private chessBoard cb;
	private static int n = 0;
	private static Vector<boolean[][]> solutions;
	private static int solutionNum = 0, currentSolution = 0;
	private JLabel label;
	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		solutions = new Vector<boolean[][]>();
		parseSolutions(args);
		
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					driver frame = new driver(n);
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public driver(int n) {
		setTitle("NQueens");
		setIconImage(Toolkit.getDefaultToolkit().getImage(driver.class.getResource("/com/sun/java/swing/plaf/motif/icons/Question.gif")));
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 525, 436);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		label = new JLabel("Solution "+(currentSolution+1)+"/"+solutionNum);
		label.setFont(new Font("Dialog", Font.BOLD, 20));
		label.setHorizontalAlignment(SwingConstants.CENTER);
		JButton next, last;
		
		next=  new JButton("Next Solution");
		next.addActionListener(this);
		next.setActionCommand("next");
		
		last = new JButton("Last Soluton");
		last.addActionListener(this);
		last.setActionCommand("last");
		
		cb = new chessBoard(n,solutions.get(0));
		contentPane.add(label,BorderLayout.SOUTH);
		contentPane.add(last,BorderLayout.WEST);
		contentPane.add(next,BorderLayout.EAST);
		contentPane.add(cb);
		this.pack();
	}

	// This method parses the text file that is supposed to be produced by Nqueens.cpp
	// It's pretty ugly, but functional
	private static void parseSolutions(String args[])
	{
		// Check that we got a value N from the c++ program (Nqueens.cpp returns N)
		if(args.length != 1)
		{
			System.err.println("ERROR, did not receive proper size argument");
		}				
		
		// Try to read file
		try {
			
			// Use NIO to read file into buffer
			RandomAccessFile solFile = new RandomAccessFile("src/queensSolutions.txt","r");
			FileChannel inChannel = solFile.getChannel();
			
			long fileSize = inChannel.size();
			
			ByteBuffer buff = ByteBuffer.allocate((int)fileSize);
			inChannel.read(buff);
			buff.rewind();
			
			// Read in N
			n = Integer.parseInt(args[0]);
			
			// Because the file could have newlines or EOF's, 
			// I need to parse it one character at a time. Unfortunately,
			// this requires a lot of counters to fill the boolean[][]'s correctly
			int solCount = 0;
			int count = 0;
			int row = 0;
			int col = 0;
			boolean temp[][] = new boolean[n][n];
			
			// The if/elses are really ugly, but I didn't want to use a switch
			// that might not be supported in non-1.7 java releases
			for(int ndx  = 0; ndx < fileSize; ndx++){
				char character = (char)buff.get();

				// A queen is here
				if(character == '1') {
					temp[row][col] = true;
					col++;
					count++;
				}
				
				// No queen
				else if(character == '0') {
					temp[row][col] = false;
					col++;
					count++;
				}
				
				// Ignore newline
				else if(character == '\n') continue;
				
				// Unknown character
				else{
					System.err.println("ERROR, Malformed solution file - extraneous characters"); System.exit(1);
				}
	
				// Manually manipulate boolean[][] indices
				if(col >= n)
				{
					row++;
					col = 0;
				}
				
				// Manually manipulate boolean[][] indices
				// Also add completed solution if necessary
				if(row >= n)
				{
					solutions.add(temp);
					row = col = 0;
					solCount++;
					temp = new boolean[n][n];
				}
			}
			
			// A 4x4 board with 3 solutions would have 48 non-newline characters
			// This number is stored in count
			if(count % (n*n) != 0)
			{
				System.err.println("ERROR, incomplete solution files - missing data");
				System.exit(1);
			}
			
			// close resources
		    inChannel.close();
		    solFile.close();
			
		    solutionNum = solCount;
		} catch (IOException e) {
			System.err.println("ERROR, could not read solution file");
			System.exit(1);
		}
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		if(arg0.getActionCommand().compareTo("next") == 0)
		{
			if(currentSolution != solutionNum-1)
			{
				currentSolution++;
				this.getContentPane().remove(cb);
				cb = new chessBoard(n,solutions.get(currentSolution));
				this.getContentPane().add(cb,BorderLayout.CENTER);
				label.setText("Solution "+(currentSolution+1)+"/"+solutionNum);
				revalidate();
				repaint();
			}			
		}
		
		else if(arg0.getActionCommand().compareTo("last") == 0)
		{
			if(currentSolution != 0)
			{
				currentSolution--;
				this.getContentPane().remove(cb);
				cb = new chessBoard(n,solutions.get(currentSolution));
				this.getContentPane().add(cb,BorderLayout.CENTER);
				label.setText("Solution "+(currentSolution+1)+"/"+solutionNum);
				revalidate();
				repaint();
			}
		}
	}
	
}
